/*
 * CommandQueue.java
 *
 * Created on 1. juni 2007, 15:00
 *         http://mail-archives.apache.org/mod_mbox/tomcat-users/200412.mbox/%3Ce030143004122123585719f4d0@mail.gmail.com%3E
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package sja.util.command;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * @author sevi
 */
public class CommandQueue {

    private static BlockingQueue<Command> queue;
    private static int consumerThreads = 10;
    protected static boolean shouldRun = true;
    private static CommandQueue commandQueue = new CommandQueue();     //only spawned so we can make inner classes behave as object even thought this class is static

    private CommandQueue() {
    }

    static {
        //init();
    }

    private static void init() {
        queue = new LinkedBlockingQueue<Command>();
        for (int i = 0; i < consumerThreads; i++) {
            CommandQueueConsumer commandQueueConsumer = commandQueue.getConsumer(queue);
            Thread t = new Thread(commandQueueConsumer, "commandQueueConsumerThread" + i);
            //t.setDaemon(true); //VM exits, when only daemon threads are left. That is what we desire, since it will give less leaks.
            t.setPriority(Thread.MIN_PRIORITY); //run in the background as a low priority thread.
            t.start();
        }
    }

    public static void add(Command command) {
        if (queue == null) init();
        queue.add(command);
    }

    public static void stop() {
        shouldRun = false;
        consumerThreads = consumerThreads;

        //queue = null;
    }

    public static int size() {
        if (queue == null) {
            return 0;
        } else {
            return queue.size();
        }
    }

    private CommandQueueConsumer getConsumer(BlockingQueue<Command> queue) {
        return new CommandQueueConsumer(queue);
    }

    private class CommandQueueConsumer implements Runnable {

        /**
         * Holds a reference to the shared queue
         */
        private BlockingQueue<Command> queue;

        /**
         * Hides the default constructor
         */
        private CommandQueueConsumer() {
        }

        public CommandQueueConsumer(BlockingQueue<Command> queue) {
            this.queue = queue;
        }

        public void run() {

            while (CommandQueue.shouldRun || !queue.isEmpty()) {  //stop the queuerunners
                Command command = null;
                try {
                    //System.out.println("Consumer: Size is " + queue.size());
                    command = queue.take();
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
                if (command != null) {
                    command.execute();
                }
            }

        }

    }

    protected static class NullCommand implements Command {
        public void execute() {
        }
    }

    protected static class EchoCommand implements Command {

        private String echo;

        /**
         * Creates a new instance of HelloCommand
         *
         * @param echoString the string to be echo'ed
         */
        public EchoCommand(String echoString) {
            this.echo = echoString;
        }

        public void execute() {
            synchronized (queue) {
                System.out.println(counter + " " + echo + "    Queue: " + queue.size() + "     Shouldrun: " + shouldRun);
                counter++;
            }
        }

    }

    private static int counter = 0;

    public static void main(String[] args) {
        for (int i = 0; i < 10 * consumerThreads; i++) {
            Command command = new EchoCommand("Hello!_" + i);
            CommandQueue.add(command);
        }

        CommandQueue.stop();
        /*
              Command command = new EchoCommand("AFTER");
              CommandQueue.add(command);
        */


    }
}
